/*
 * Dependency-Check Plugin for SonarQube
 * Copyright (C) 2015-2023 dependency-check
 * philipp.dallig@gmail.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.dependencycheck.parser.element;

import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.dependencycheck.base.DependencyCheckUtils;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

@JsonIgnoreProperties({"notes", "references", "vulnerableSoftware", "unscored", "knownExploitedVulnerability"})
public class Vulnerability {

    private static final Logger LOGGER = Loggers.get(Vulnerability.class);

    private final String name;
    private final String source;
    private final String severity;
    private final String description;
    private final CvssV2 cvssv2;
    private final CvssV3 cvssv3;
    private final String[] cwes;

    @JsonCreator
    public Vulnerability(@JsonProperty(value = "name", required = true) @NonNull String name,
                         @JsonProperty(value = "source", required = true) @NonNull String source,
                         @JsonProperty(value = "description", required = true) @NonNull String description,
                         @JsonProperty(value = "cwes") @Nullable String[] cwe,
                         @JsonProperty(value = "cvssv2") @JacksonXmlProperty(localName="cvssV2") @Nullable CvssV2 cvssv2,
                         @JsonProperty(value = "cvssv3") @JacksonXmlProperty(localName="cvssV3") @Nullable CvssV3 cvssv3,
                         @JsonProperty(value = "severity") @Nullable String severity) {
        this.name = name;
        this.source = source;
        this.severity = severity;
        this.description = description.trim();
        this.cwes = cwe;
        this.cvssv2 = cvssv2;
        this.cvssv3 = cvssv3;
    }

    @NonNull
    public String getName() {
        return name;
    }

    @NonNull
    public String getSource() {
        return source;
    }

    @NonNull
    public Float getCvssScore() {
        return getCvssScore(true);
    }

    @NonNull
    public Float getCvssScore(boolean preferCvssv3) {
        Cvss cvss;
        if (preferCvssv3) {
            cvss = getCvssV3().orElse(getCvssV2().orElse(null));
        } else {
            cvss = getCvssV2().orElse(getCvssV3().orElse(null));
        }
        if (cvss != null) {
            return cvss.getScore();
        }
        return DependencyCheckUtils.severityToCVSSScore(getSeverity(preferCvssv3));
    }

    @NonNull
    public String getSeverity() {
        return getSeverity(true);
    }

    @NonNull
    public String getSeverity(boolean preferCvssv3) {
        /*
         * severity can be Unknown
         * https://github.com/jeremylong/DependencyCheck/commit/1dd037fa5ccfb3145817c9d5aec3263a5b2145c4
         */
        boolean unscoredSeverity = StringUtils.equalsIgnoreCase("Unknown", severity);
        if (severity != null && !unscoredSeverity) {
            return severity;
        }
        Cvss cvss;
        if (preferCvssv3) {
            cvss = getCvssV3().orElse(getCvssV2().orElse(null));
        } else {
            cvss = getCvssV2().orElse(getCvssV3().orElse(null));
        }
        if (cvss != null) {
            return cvss.getSeverity();
        }
        if (unscoredSeverity) {
            LOGGER.warn("vulnerability {} is unscored. Using MEDIUM", name);
        } else {
            LOGGER.warn("vulnerability {} without severity. Using MEDIUM", name);
        }
        return "MEDIUM";
    }

    @NonNull
    public String getDescription() {
        return description;
    }

    public Optional<String[]> getCwes() {
        return Optional.ofNullable(cwes);
    }
    /**
     * @return the cvssV2
     */
    public Optional<Cvss> getCvssV2() {
        return Optional.ofNullable(cvssv2);
    }
    /**
     * @return the cvssV3
     */
    public Optional<Cvss> getCvssV3() {
        return Optional.ofNullable(cvssv3);
    }

}
